﻿using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Data.Entity.ModelConfiguration.Conventions;
using System.Linq;
using System.Linq.Expressions;
using System.Runtime.Remoting.Messaging;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using MyBlog.DataAccessInterface;
using MyBlog.DataEntity;

namespace MyBlog.DataAccess
{
    public class BlogContext : DbContext, IContext, IUnitOfWork
    {
        public BlogContext()
            : base("name=BlogDB")
        {
            //表示每次都重新创建数据库。适用于较小的项目前期开发。数据库和模型经常变动。使用的几率较小。
            //Database.SetInitializer(new DropCreateDatabaseAlways<BlogContext>());

            //表示每次模型改变时都重新创建数据库。项目前期开发，数据库和模型变动比较多。
            //Database.SetInitializer(new DropCreateDatabaseIfModelChanges<BlogContext>());

            //禁用初始化，不会自动创建数据库
            Database.SetInitializer<BlogContext>(null);
        }

        /// <summary>
        /// 在一次请求过程中只创建一次EF上下文实例
        /// </summary>
        BlogContext CurrentContext
        {
            get
            {
                var dbContext = CallContext.GetData(typeof(BlogContext).Name) as BlogContext;
                if (dbContext != null) return dbContext;
                dbContext = new BlogContext();
                //将新创建的 ef上下文对象 存入线程
                CallContext.SetData(typeof(BlogContext).Name, dbContext);
                return dbContext;
            }
        }

        #region DBEntities and Mapping
        public DbSet<Tbl_Account> Tbl_Account { set; get; }
        public DbSet<Tbl_Album> Tbl_Album { set; get; }
        public DbSet<Tbl_AlbumDetail> Tbl_AlbumDetail { set; get; }
        public DbSet<Tbl_Article> Tbl_Article { set; get; }
        public DbSet<Tbl_ArticleComment> Tbl_ArticleComment { set; get; }
        public DbSet<Tbl_Bug> Tbl_Bug { set; get; }
        public DbSet<Tbl_Category> Tbl_Category { set; get; }
        public DbSet<Tbl_Header> Tbl_Header { set; get; }
        public DbSet<Tbl_Message> Tbl_Message { set; get; }
        public DbSet<Tbl_Mood> Tbl_Mood { set; get; }
        public DbSet<Tbl_MoodComment> Tbl_MoodComment { set; get; }
        public DbSet<Tbl_MsgReply> Tbl_MsgReply { set; get; }
        public DbSet<Tbl_PicComment> Tbl_PicComment { set; get; }
        public DbSet<Tbl_SysLog> Tbl_SysLog { set; get; }
        public DbSet<Tbl_Visitor> Tbl_Visitor { set; get; }

        public DbSet<Tbl_SysHistory> Tbl_SysHistory { set; get; }

        public DbSet<Tbl_Words> Tbl_Words { set; get; }

        public DbSet<Tbl_WordComment> Tbl_WordComment { set; get; }

        public DbSet<Tbl_SysInfo> Tbl_SysInfo { set; get; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {
            #region add model map config
            modelBuilder.Configurations.Add(new AccountMap());
            modelBuilder.Configurations.Add(new AlbumMap());
            modelBuilder.Configurations.Add(new AlbumDetailMap());
            modelBuilder.Configurations.Add(new ArticleMap());
            modelBuilder.Configurations.Add(new ArticleCommentMap());
            modelBuilder.Configurations.Add(new MessageMap());
            modelBuilder.Configurations.Add(new MoodMap());
            modelBuilder.Configurations.Add(new MoodCommentMap());
            modelBuilder.Configurations.Add(new PicCommentMap());

            modelBuilder.Entity<Tbl_Header>()
                .HasMany(e => e.Tbl_Account)
                .WithRequired(e => e.Tbl_Header)
                .HasForeignKey(e => e.HeadId)
                .WillCascadeOnDelete(false);

            modelBuilder.Entity<Tbl_MsgReply>()
                  .HasMany(e => e.MsgReplyList)
                  .WithOptional(e => e.Parent)
                  .HasForeignKey(e => e.ParentID)
                  .WillCascadeOnDelete(false);

            modelBuilder.Entity<Tbl_Bug>()
                 .HasMany(e => e.BugList)
                 .WithOptional(e => e.Parent)
                 .HasForeignKey(e => e.ParentID)
                 .WillCascadeOnDelete(false);

            modelBuilder.Entity<Tbl_Words>()
                .HasMany(e => e.Tbl_WordComment)
                .WithRequired(e => e.Tbl_Words)
                .HasForeignKey(e => e.WID)
                .WillCascadeOnDelete(false);

            modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
            #endregion
        }
        #endregion

        /// <summary>
        /// 将所有更改保存到数据库
        /// </summary>
        public void Commit()
        {
            CurrentContext.SaveChanges();
        }

        public void Add<T>(T entity) where T : class
        {
            CurrentContext.Set<T>().Add(entity);
        }

        public void AddRange<T>(IEnumerable<T> entities) where T : class
        {
            foreach (var item in entities)
            {
                CurrentContext.Add<T>(item);
            }
        }

        public void Update<T>(T entity) where T : class
        {
            //Detached(entity);
            var t = CurrentContext.Set<T>().Attach(entity);
            CurrentContext.Entry<T>(t).State = EntityState.Modified;
        }

        public void Delete<T>(params object[] primaryKey) where T : class
        {
            var item = CurrentContext.Set<T>().Find(primaryKey);
            CurrentContext.Set<T>().Remove(item);
        }

        public void Delete<T>(Expression<Func<T, bool>> keySelector) where T : class
        {
            var items = CurrentContext.Set<T>().Where(keySelector);
            foreach (var item in items)
            {
                CurrentContext.Set<T>().Remove(item);
            }
        }

        public void DeleteRange<T>(IEnumerable<T> entities) where T : class
        {
            foreach (var item in entities)
            {
                CurrentContext.Set<T>().Attach(item);
                CurrentContext.Set<T>().Remove(item);
            }
        }

        public T Get<T>(params object[] primaryKey) where T : class
        {
            return CurrentContext.Set<T>().Find(primaryKey);
        }

        public T Get<T>(Expression<Func<T, bool>> keySelector) where T : class
        {
            return CurrentContext.Set<T>().FirstOrDefault(keySelector);
        }

        public List<T> GetAll<T>(bool isTracking = true) where T : class
        {
            isTracking = true;
            IQueryable<T> query;
            if (!isTracking)
            {
                query = CurrentContext.Set<T>().AsNoTracking().AsQueryable();
            }
            else
            {
                query = CurrentContext.Set<T>().AsQueryable();
            }
            return query.ToList();
        }

        public List<T> GetAll<T>(Expression<Func<T, bool>> keySelector, bool isTracking = true) where T : class
        {
            isTracking = true;
            IQueryable<T> query;
            if (!isTracking)
            {
                query = CurrentContext.Set<T>().AsNoTracking().Where(keySelector);
            }
            else
            {
                query = CurrentContext.Set<T>().Where(keySelector);
            }
            return query.ToList();
        }

        public List<T> GetAll<T>(Expression<Func<T, bool>> keySelector, int page, int size, out int total, bool isTracking = true) where T : class
        {
            isTracking = true;
            IQueryable<T> query;
            if (!isTracking)
            {
                query = CurrentContext.Set<T>().AsNoTracking().Where(keySelector);
            }
            else
            {
                query = CurrentContext.Set<T>().Where(keySelector);
            }
            total = query.Count();
            return query.Skip((page - 1) * size).Take(size).ToList();
        }

        public List<T> SqlQuery<T>(string sql, params object[] prms) where T : class
        {
            var list = CurrentContext.Database.SqlQuery<T>(sql, prms).ToList();
            return list;
        }

        public int ExcuateSql(string sql, params object[] prms)
        {
            int count = CurrentContext.Database.ExecuteSqlCommand(sql, prms);
            return count;
        }

        /// <summary>
        /// 移除实体跟踪状态
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="entity"></param>
        public void Detached<T>(T entity) where T : class
        {
            if (CurrentContext.Entry(entity).State != EntityState.Detached)
            {
                CurrentContext.Entry(entity).State = EntityState.Detached;
            }
        }
    }
}
